/*
 * author	: calvin
 * email	: calvinwilliams@163.com
 *
 * Licensed under the LGPL v2.1, see the file LICENSE in base directory.
 */

#ifndef _H_CDBC_
#define _H_CDBC_

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <locale.h>
#include <time.h>

int asprintf(char **strp, const char *fmt, ...);

#ifndef DLLEXPORT
#if defined(__linux__)
#define DLLEXPORT	extern
#elif defined(_WIN32)
#define DLLEXPORT	__declspec(dllexport)
#endif
#endif

#ifndef TLS
#if ( defined __linux__ ) || ( defined __unix ) || ( defined _AIX )
#define TLS             __thread
#elif ( defined _WIN32 )
#define TLS             __declspec( thread )
#endif
#endif

#if defined(_WIN32)
#define PATH_MAX	MAX_PATH
#endif

#ifndef PRIptrdiff
#if defined(__linux__)
#define PRIptrdiff		"td"
#elif defined(_WIN32)
#define PRIptrdiff		"I64d"
#endif
#endif

#ifndef STRDUP
#if ( defined __linux ) || ( defined __unix )
#define STRDUP	strdup
#define STRNDUP	strndup
#elif ( defined _WIN32 )
#define STRDUP	_strdup
#define STRNDUP	_cdbc_strndup
#endif
#endif

#if defined(_WIN32)
DLLEXPORT char *_cdbc_strndup(const char *s, size_t n);
#endif

#ifndef STRCMP
#define STRCMP(_a_,_C_,_b_) ( strcmp(_a_,_b_) _C_ 0 )
#define STRNCMP(_a_,_C_,_b_,_n_) ( strncmp(_a_,_b_,_n_) _C_ 0 )
#define STRNNCMP(_a_,_an_,_C_,_b_,_bn_)	( \
						!! ( \
							( \
								(_an_) == 0 && (_bn_) == 0 \
							) \
							|| \
							( \
								(_an_) == (_bn_) && strncmp(_a_,_b_,_an_) == 0 \
							) \
						) \
					) _C_ 1
#endif

#ifndef STRICMP
#if ( defined _WIN32 )
#define STRICMP(_a_,_C_,_b_) ( _stricmp(_a_,_b_) _C_ 0 )
#define STRNICMP(_a_,_C_,_b_,_n_) ( _strnicmp(_a_,_b_,_n_) _C_ 0 )
#define STRISTR(_a_,_b_) _cdbc_stristr(_a_,_b_)
#elif ( defined __unix ) || ( defined _AIX ) || ( defined __linux__ )
#define STRICMP(_a_,_C_,_b_) ( strcasecmp(_a_,_b_) _C_ 0 )
#define STRNICMP(_a_,_C_,_b_,_n_) ( strncasecmp(_a_,_b_,_n_) _C_ 0 )
char *strcasestr(const char *haystack, const char *needle);
#define STRISTR(_a_,_b_) strcasestr(_a_,_b_)
#endif
#endif

#if defined(_WIN32)
DLLEXPORT char *_cdbc_stristr(const char* str, const char* substr);
#endif

#ifndef MEMCMP
#define MEMCMP(_a_,_C_,_b_,_n_) ( memcmp(_a_,_b_,_n_) _C_ 0 )
#endif

#if defined(_WIN32)
char *strptime(const char *buf, const char *fmt, struct tm *tm);
#endif

#define CDBC_ERROR_PARAMETER			-1
	
#define CDBC_ERROR_ALLOC			-13

#define CDBC_ERROR_CONNECT			-21
#define CDBC_ERROR_PREAPRE			-22
#define CDBC_ERROR_BIND				-23
#define CDBC_ERROR_QUERY			-24
#define CDBC_ERROR_BEGINTRANSACTION		-31
#define CDBC_ERROR_COMMITTRANSACTION		-32
#define CDBC_ERROR_ROLLBACKTRANSACTION		-33
#define CDBC_ERROR_BIND_PARAM			-41

#define CDBC_ERROR_FILE_NOT_FOUND		-71
#define CDBC_ERROR_NO_FUNCTION_IN_DRIVER	-72
#define CDBC_ERROR_FIELD_TYPE_NOT_SUPPORT	-73
#define CDBC_ERROR_INIT_FAILED_IN_DRIVER	-74
#define CDBC_ERROR_BIND_ARRAY_COUNT_NOT_MATCHED	-75

#define CDBC_DATABASE_TYPE_MYSQL		"MySQL"
#define CDBC_DATABASE_TYPE_POSTGRESQL		"PostgreSQL"
#define CDBC_DATABASE_TYPE_SQLITE		"Sqlite"
#define CDBC_DATABASE_TYPE_ORACLE		"Oracle"

struct DatabaseDriver ;
struct DatabaseConnection ;

enum FieldType
{
	CDBC_FIELDTYPE_INVALID = 0 ,
	CDBC_FIELDTYPE_INT8 = 1008 ,
	CDBC_FIELDTYPE_INT16 = 1016 ,
	CDBC_FIELDTYPE_INT32 = 1032 ,
	CDBC_FIELDTYPE_INT64 = 1064 ,
	CDBC_FIELDTYPE_FLOAT = 2032 ,
	CDBC_FIELDTYPE_DOUBLE = 2064 ,
	CDBC_FIELDTYPE_DECIMAL = 2199 ,
	CDBC_FIELDTYPE_CHAR = 4008 ,
	CDBC_FIELDTYPE_VARCHAR = 4016 ,
	CDBC_FIELDTYPE_DATE = 5000 ,
	CDBC_FIELDTYPE_TIME = 5100 ,
	CDBC_FIELDTYPE_DATETIME = 5400 ,
	CDBC_FIELDTYPE_TIMESTAMP = 5600 ,
	CDBC_FIELDTYPE_OTHER = 9999 ,
} ;

struct FieldInfo
{
	char		*field_name ;
	enum FieldType	field_type ;
	uint32_t	field_length ;
	uint32_t	field_decimal_length ;
} ;

struct FieldBind
{
	uint32_t	buffer_type ;
	char		*buffer ;
	char		*buffer_alloced ;
	uint32_t	buffer_length ;
} ;

#define CDBC_FUNCNAME_INITDRIVERLIBRARY		"InitDriverLibrary"
#define CDBC_FUNCNAME_ENDDRIVERLIBRARY		"EndDriverLibrary"
#define CDBC_FUNCNAME_CONNECTTODATABASE		"ConnectToDatabase"
#define CDBC_FUNCNAME_DISCONNECTFROMDATABASE	"DisconnectFromDatabase"
#define CDBC_FUNCNAME_EXECUTESQL		"ExecuteSql"
#define CDBC_FUNCNAME_AUTOCOMMITTRANSACTION	"AutoCommitTransaction"
#define CDBC_FUNCNAME_BEGINTRANSACTION		"BeginTransaction"
#define CDBC_FUNCNAME_COMMITTRANSACTION		"CommitTransaction"
#define CDBC_FUNCNAME_ROLLBACKTRANSACTION	"RollbackTransaction"
typedef int funcInitDriverLibrary();
typedef int funcEndDriverLibrary();
typedef struct DatabaseConnection *funcConnectToDatabase( char *db_host , int db_port , char *db_user , char *db_pass , char *db_name );
typedef void funcDisconnectFromDatabase( struct DatabaseConnection **conn );
typedef void funcExecuteSql( struct DatabaseConnection *db_conn , char *sql , struct FieldBind *binds_array , int binds_array_length , int *row_count , int *col_count , struct FieldInfo **query_field_set , char ***query_result_set , int *affected_count );
typedef void funcAutoCommitTransaction( struct DatabaseConnection *db_conn , unsigned char enable_autocommit );
typedef void funcBeginTransaction( struct DatabaseConnection *db_conn );
typedef void funcCommitTransaction( struct DatabaseConnection *db_conn );
typedef void funcRollbackTransaction( struct DatabaseConnection *db_conn );

DLLEXPORT struct DatabaseDriver *DBCGetDatabaseDriver( char *db_type_name );
DLLEXPORT char *DBCGetDatabaseDriverName( struct DatabaseDriver *db_driver );
DLLEXPORT void DBCUnloadAllDatabaseDrivers();

DLLEXPORT struct DatabaseConnection *DBCConnectToDatabase( struct DatabaseDriver *db_driver , char *db_host , int db_port , char *db_user , char *db_pass , char *db_name );
DLLEXPORT void DBCDisconnecFromDatabase( struct DatabaseDriver *db_driver , struct DatabaseConnection **db_conn );

DLLEXPORT void DBCExecuteSql( struct DatabaseDriver *db_driver , struct DatabaseConnection *db_conn , char *sql , struct FieldBind *binds_array , int binds_array_length , int *row_count , int *col_count , struct FieldInfo **query_field_set , char ***query_result_set , int *affected_count );
DLLEXPORT void DBCFreeSqlResult( struct FieldInfo **query_field_set , char ***query_result_set );

DLLEXPORT void DBCAutoCommitTransaction( struct DatabaseDriver *db_driver , struct DatabaseConnection *db_conn , unsigned char enable_autocommit );
DLLEXPORT void DBCBeginTransaction( struct DatabaseDriver *db_driver , struct DatabaseConnection *db_conn );
DLLEXPORT void DBCCommitTransaction( struct DatabaseDriver *db_driver , struct DatabaseConnection *db_conn );
DLLEXPORT void DBCRollbackTransaction( struct DatabaseDriver *db_driver , struct DatabaseConnection *db_conn );

DLLEXPORT void DBCSetLastErrno( int last_errno );
DLLEXPORT int DBCGetLastErrno();
DLLEXPORT void DBCSetLastNativeErrno( long last_native_errno );
DLLEXPORT long DBCGetLastNativeErrno();
DLLEXPORT void DBCSetLastNativeError( char *last_error_str );
DLLEXPORT char *DBCGetLastNativeError();
DLLEXPORT void DBCSetLastSqlState( char *last_sqlstate );
DLLEXPORT char *DBCGetLastSqlState();

struct ResizableBuffer
{
	size_t		increase_size ;
	
	char		*buf_base ;
	char		*buf_ptr ;
	uint32_t	data_length ;
	uint32_t	buf_size ;
} ;

#define RESIZABLEBUFFER_0		{ 0 }
#define IS_RESIZABLEBUFFER_0(_rb_)	( (_rb_).increase_size==0 )

int InitResizableBuffer( struct ResizableBuffer *rb , size_t init_size , size_t increase_size );
void CleanResizableBuffer( struct ResizableBuffer *rb );

char *AppendResizableBuffer( struct ResizableBuffer *rb , char *data , uint32_t data_len , char **rebase );

#endif

